package com.nononsenseapps.feeder.ui.common.htmlview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatTextView;

import io.noties.markwon.base.image.span.AsyncDrawableSpan;
import io.noties.markwon.listener.MediaClickListener;
import io.noties.markwon.listener.UrlClickListener;
import io.noties.markwon.span.ClickableAudioSpan;

import java.util.Arrays;
import java.util.List;

import io.noties.markwon.base.html.iframe.AsyncIframeDrawableSpan;
import io.noties.markwon.base.image.AsyncRecycleDrawable;
import io.noties.markwon.base.image.span.AsyncRecycleDrawableSpan;
import io.noties.markwon.base.html.video.AsyncVideoDrawableSpan;
import timber.log.Timber;

/**
 * A textview containing clickable links.
 */
public class HtmlTextView extends AppCompatTextView {



    public HtmlTextView(Context context) {
        super(context);
    }

    public HtmlTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HtmlTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    private MediaClickListener mediaClickListener;
    private UrlClickListener urlClickListener;
    List<AsyncRecycleDrawableSpan> allImageSpanList = null;

    @Override
    public void setText(CharSequence text, BufferType type) {
        allImageSpanList=null;
        if (text instanceof Spanned) {
            AsyncRecycleDrawableSpan[] allImageSpanArray= ((Spanned) text).getSpans(0, ((Spanned) text).length(), AsyncRecycleDrawableSpan.class);
            allImageSpanList=Arrays.asList(allImageSpanArray);
        }
        super.setText(text, BufferType.SPANNABLE);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        TextView widget = this;
        Object text = widget.getText();
        if (text instanceof Spanned) {
            Spanned buffer = (Spanned) text;

            int action = event.getAction();

            if (action == MotionEvent.ACTION_UP || action == MotionEvent
                    .ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();

                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();

                x += widget.getScrollX();
                y += widget.getScrollY();

                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);

                    if (x <= layout.getLineRight(line)) {
                        int off = layout.getOffsetForHorizontal(line, x);

                        if(mediaClickListener!=null) {
                            AsyncVideoDrawableSpan[] video = buffer.getSpans(off, off, AsyncVideoDrawableSpan.class);
                            if (video.length != 0) {
                                if (action == MotionEvent.ACTION_UP) {
                                    mediaClickListener.onVideoClickListener(video[0].getVideoSrc());
                                }
                                // Don't allow selections on this so always return true
                                return true;
                            }

                            ClickableAudioSpan[] audio = buffer.getSpans(off, off, ClickableAudioSpan.class);
                            if (audio.length != 0) {
                                if (action == MotionEvent.ACTION_UP) {
                                    mediaClickListener.onAudioClickListener(audio[0].getSource());
                                }
                                // Don't allow selections on this so always return true
                                return true;
                            }

                            AsyncIframeDrawableSpan[] iframe = buffer.getSpans(off, off, AsyncIframeDrawableSpan.class);
                            if (iframe.length != 0) {
                                if (action == MotionEvent.ACTION_UP) {
                                    mediaClickListener.onIframeClickListener(iframe[0].getDestination());
                                }
                                // Don't allow selections on this so always return true
                                return true;
                            }

                            AsyncDrawableSpan[] image = buffer.getSpans(off, off, AsyncDrawableSpan.class);
                            if (image.length != 0) {
                                if (action == MotionEvent.ACTION_UP) {
                                    mediaClickListener.onImageclickListener(image[0].getDrawable().getDestination());
                                }
                                // Don't allow selections on this so always return true
                                return true;
                            }
                        }
                        URLSpan[] link =buffer.getSpans(off, off, URLSpan.class);
                        if (link.length != 0) {
                            if (action == MotionEvent.ACTION_UP) {
                                //link[0].onClick(widget);
                                if(urlClickListener!=null)
                                    urlClickListener.onUrlClicked(link[0].getURL());
                            }
                            return true;
                        }
                    }
            }
        }

        return super.onTouchEvent(event);
    }

    public void setMediaClickListener(MediaClickListener mediaClickListener) {
        this.mediaClickListener = mediaClickListener;
    }

    public void setUrlClickListener(UrlClickListener urlClickListener) {
        this.urlClickListener = urlClickListener;
    }

    public void scrollToLine(int line){
        scrollTo(0,getLayout().getLineTop(line));
    }

    private final int delayMillis = 150;

    private Runnable scrollEndTask ;

    protected void onScrollStart(){
         Timber.d("onScrollStart ...");
    }

    protected void onScrollEnd(){
        Timber.d("onScrollEnd ...");
        if(allImageSpanList==null||allImageSpanList.isEmpty()) return ;
        updateScrollSpanned();
    }

    @Override
    public void scrollTo(int x, int y) {
        srcollDown=getScrollY()>=y;
        super.scrollTo(x, y);
        if(allImageSpanList==null||allImageSpanList.isEmpty()) return ;
        if(scrollEndTask == null){
            onScrollStart();
            scrollEndTask=new Runnable() {
                @Override
                public void run() {
                    scrollEndTask = null;
                    onScrollEnd();
                }
            };
            postDelayed(scrollEndTask,delayMillis);
        }else{
            removeCallbacks(scrollEndTask);
            postDelayed(scrollEndTask,delayMillis);
        }
    }

    int viewHight=0;
    int parentStatY=0;
    boolean srcollDown = true;

    public void parentScrollChanged(int startX,int startY,int oldY,int viewHight){
        if(allImageSpanList==null||allImageSpanList.isEmpty()) return ;
        this.viewHight = viewHight;
        this.parentStatY=startY;
        this.srcollDown=startY>=oldY;
        if(scrollEndTask == null){
            onScrollStart();
            scrollEndTask=new Runnable() {
                @Override
                public void run() {
                    scrollEndTask = null;
                    onScrollEnd();
                }
            };
            postDelayed(scrollEndTask,delayMillis);
        }else{
            removeCallbacks(scrollEndTask);
            postDelayed(scrollEndTask,delayMillis);
        }
    }


    public void  updateScrollSpanned(){
        Object text = getText();
        Timber.d("updateScrollSpanned ...");
        if (text instanceof Spanned) {
            Spanned buffer = (Spanned) text;

            int x = this.getScrollX();
            int y = parentStatY-getTop();

            Timber.d("updateScrollSpanned scrollX:%d,scrollY:%d,viewHight:%d",x,y,viewHight);

            Layout layout = this.getLayout();
            int startLine =0;
            int endLine = 0;

            if(srcollDown){
                 startLine = layout.getLineForVertical(y - viewHight);
                 endLine = layout.getLineForVertical(y + viewHight*2);
            }else{
                startLine = layout.getLineForVertical(y - viewHight);
                endLine = layout.getLineForVertical(y + viewHight*2);
            }

            int startOff = layout.getOffsetForHorizontal(startLine, x);
            int endOff = layout.getOffsetForHorizontal(endLine, x);

            AsyncRecycleDrawableSpan[] imageSpanArray = buffer.getSpans(startOff, endOff, AsyncRecycleDrawableSpan.class);
            List<AsyncRecycleDrawableSpan> imageSpanlist = Arrays.asList(imageSpanArray);

            Timber.d("updateScrollSpanned onstartOff:%d,endOff:%d,in imageSpan:%d,all imageSpan:%d", startOff,endOff,imageSpanArray.length,allImageSpanList.size());

            for(AsyncRecycleDrawableSpan showDrawableSpan:imageSpanlist) {
                AsyncRecycleDrawable drawable=showDrawableSpan.getDrawable();
                /*if(drawable.getLoadState() == AsyncRecycelDrawableState.RECYCLE)
                    mustDraw=true;*/
                drawable.reLoad();
            }

            for(AsyncRecycleDrawableSpan drawableSpan:allImageSpanList){
                if(!imageSpanlist.contains(drawableSpan)){
                    AsyncRecycleDrawable drawable = drawableSpan.getDrawable();
                    drawable.recycle();
                }
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        try {
            super.onDraw(canvas);
        }catch (RuntimeException e){
            Timber.d(e);
            Spanned spanString= (Spanned) this.getText();
            AsyncDrawableSpan[] images =spanString.getSpans(0,getText().length() , AsyncDrawableSpan.class);
            for(AsyncDrawableSpan image:images){
                image.getDrawable().clearResult();
            }
            invalidate();
        }
    }
}
